
# Online Python - IDE, Editor, Compiler, Interpreter

import random


class Unit:
    
    @property
    def is_row(self):
        return not self.parent.is_row
    
    @property
    def width(self):
        if self.is_row:
            return self.parent.width
        return self.parent.get_child_length(self.index)
    
    @property
    def height(self):
        if self.is_row:
            return self.parent.get_child_length(self.index)
        return self.parent.height

    @property
    def area(self):
        return self.width * self.height

    @property
    def position(self)->tuple:
        parent_position = self.parent.position
        if self.is_row:
            local_y = self.parent.separations[self.index] * self.parent.height
            return parent_position[0], parent_position[1] + local_y
        else:
            local_x = self.parent.separations[self.index] * self.parent.width
            return parent_position[0] + local_x, parent_position[1]

    def rhino_macro(
        self,
        show_relative_position=True,
        show_function_id=False,
        color:str=None,
        text_height=10,
        shift:tuple[float, float]=None):

        position = (self.position[0] + shift[0] if shift else 0, self.position[1] + shift[1] if shift else 0)
        width = self.width
        height = self.height

        texts:list[str] = []
        if show_relative_position:
            texts.append(self.relative_position)

        if show_function_id:
            texts.append(str(self.function_id))

        macros = [
            f"_Rectangle {position[0]},{position[1]} {position[0] + width},{position[1] + height}"
        ]

        if color:
            macros.append("_selLast")
            macros.append("-hatch _EnterEnd")
            macros.append("_selLast")
            macros.append(f"-properties o c o {color} _EnterEnd")

        if texts:
            center_x = position[0] + width / 2
            center_y = position[1] + height / 2
            # -text b 居中 h 10 你好\n世界 0,0
            texts_str = '\n'.join(texts)
            text = f"-text b 居中 h {text_height} {texts_str} {center_x},{center_y}"
            macros.append(text)

        return "\n".join(macros)

    @property
    def relative_position(self):
        rt = self.parent.relative_position + ("r" if self.is_row else "c") + str(self.index)
        return rt

        
    @property
    def length(self):
        if self.is_row:
            return self.width
        return self.height
        
    def get_child_length(self, index):
        if index > len(self.separations):
            raise ValueError(f"{index} out of range {len(self.separations)}")
        return self.length * (self.separations[index+1] - self.separations[index])
        
    @property
    def index(self):
        return self.parent.children.index(self)
    
    def __init__(self, parent, function_id=None):
        self.function_id = -1
        self.separations = []
        self.children:list[Unit] = []
        self.parent:Unit = parent
        self.id = random.randrange(10000)
        if function_id:
            self.function_id = function_id
        
    def separate(self, at:float):
        if not self.children:
            self.children.append(
                Unit(self)    
            )
            self.separations.clear()
            self.separations += [0, 1]

        if at not in self.separations:
            self.separations.append(at)
            self.separations.sort()
            index = self.separations.index(at)
            new = Unit(self)
            self.children.insert(index, new)
        else:
            raise ValueError

        return self

    def delete(self, at:int):
        if at == 0:
            self.separations.pop(1)
            self.children.pop(at)
        else:
            self.separations.pop(at)
            self.children.pop(at)

            
    def state(self):
        print(f"Unit-{self.id} state: {'-'.join([f'{i[0]}-{i[1]}' for i in zip(self.separations[:-1], self.children)])}-1")
        
    def __repr__(self):
        return f"<Unit id:{self.id} fid:{self.function_id}>"

    def __getitem__(self, item:int):
        return self.children[item]

    def __len__(self):
        return len(self.children)

    def tree(self):
        return str(self) + f" ({'row' if self.is_row else 'column'})\n" + self.tab_upper(
            "\n".join([
                i.tree() if i.children
                else str(i)
                for i in self.children
            ])
        )

    def function_unit_list(self):
        l = []
        for i in self.children:
            if not i.children:
                l.append(i)
            else:
                l += i.function_unit_list()
        return l

    def unit_list(self):
        l:list[Unit] = []
        for i in self.children:
            if i.children:
                l += i.unit_list()
            l.append(i)
        return l

    @staticmethod
    def tab_upper(string):
        prefix = "  ├ "
        prefix2 = "  ├   " # 消解多重 tab prefix
        prefix3 = "  │   "
        return prefix + string.replace("\n", "\n" + prefix).replace(prefix2, prefix3)


class RootUnit(Unit):
    @property
    def is_row(self):
        return self.__is_row
    @property
    def width(self):
        return self.__width
    @property
    def height(self):
        return self.__height

    @property
    def position(self) -> tuple:
        return 0, 0

    @property
    def relative_position(self):
        return ""

    @property
    def function_unit_length(self):
        return len(self.function_unit_list())

    @property
    def unit_length(self):
        return len(self.unit_list())

    def unit_list(self):
        l = super().unit_list()
        l.append(self)
        return l

    def draw_in_rhino(
        self,
        show_function_id=False,
        show_relative_position=False,
        colors:list[str]=None,
        shift:tuple[float, float]=None,
        copy_into_clipboard=True,
        caption:str=None,
        text_height=10):

        from pyperclip import copy

        macros = [
            i.rhino_macro(
                show_function_id=show_function_id,
                show_relative_position=show_relative_position,
                color = colors[i.function_id] if colors else None,
                shift=shift,
                text_height=text_height,
            )
            for i in self.function_unit_list()
        ]

        if caption:
            macros.append(
                f'-text b 居中 h {text_height} "{caption}" {self.__width/2+shift[0]},{self.__height+20+shift[1]}'
            )

        string = "\n".join(macros)

        if copy_into_clipboard:
            copy(string)

        return string

        
    def __init__(self, is_row, width, height):
        super().__init__(None)
        self.__is_row = is_row
        self.__width = width
        self.__height = height




if __name__ == '__main__':
    # root = RootUnit(True, 300, 150)
    # root.separate(0.2).separate(0.5).separate(0.7)
    # root[1].separate(0.3).separate(0.6)
    # # root[1][0].separate(0.5)
    # root[3].separate(0.7)

    root = RootUnit(False, 300, 150)
    root.separate(0.3)
    root[0].separate(0.7)
    root[1].separate(0.2).separate(0.8)
    root[1][1].separate(0.5)

    # root = RootUnit(True, 300, 150)
    # root.separate(0.7)
    # root[0].separate(0.8)
    # root[0][0].separate(0.8)
    # root[0][0][0].separate(0.7)
    # root[0][0][0][0].separate(0.3)
    # root[0][0][0][0][1].separate(0.5)

    print(root.tree())

    root.draw_in_rhino()
    print(root.function_unit_length)
    print(root.unit_length)
    print(root.unit_list())